package scnucxy;

import org.apache.commons.codec.binary.Base64;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.junit.Test;
import sun.font.Font2D;
import sun.font.Font2DHandle;
import sun.font.TrueTypeFont;
import sun.font.TrueTypeGlyphMapper;

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;

/**
 * 爬虫加密字体解密
 * 爬虫工具：jsoup
 * 测试网站：58房产
 * 注意：每次请求数据，字体文件都会变化，因此字体解码工具只能用于当前请求的页面数据
 * @author scnucxy
 * @create 2019-11-29 17:45
 */

public class SpiderDecodeFont {

    @Test
    public void testDecomeFont() throws IOException {
        //获取文档
        String url = "https://su.58.com/zufang/pn2/?PGTID=0d300008-0000-5965-8ce1-1873463f7758&ClickID=1";
        Document doc = Jsoup.connect(url).get();

        //获取key
        //定位字符串位置的script
        Elements elements =  doc.head().getElementsByTag("script");
        String s = elements.get(1).toString();
        //定位字符串
        int startPos = s.indexOf("base64,")+"base64,".length();
        int endPos = s.indexOf("') format('truetype')");
        String key = s.substring(startPos,endPos);
        System.out.println("字体base64:"+key);


        //获取一个页面上加密的数据，默认测试第一个
        Elements divElements =  doc.body().select("ul.house-list li.house-cell");
//        System.out.println(doc.toString());
        String encodeString =  divElements.get(0).select("div.money .strongbox").text();


        //解密
        System.out.println("房屋："+doc.body().select("ul.house-list li.house-cell").get(0).select("a").text());
        System.out.printf("原数据:%s 解密后数据:%s",encodeString,decodeString(key,encodeString));
    }

    /**
     * font-secret字符串专用解密工具
     *
     * @param key          密匙
     * @param encodeString 加密后的字符串
     * @return 解密后的字符串
     */
    public static String decodeString(String key, String encodeString) {
        try {
            //base64解码，初始化字体
            byte[] ss = Base64.decodeBase64(key);
            InputStream inputStream = new ByteArrayInputStream(ss);
            Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, inputStream);
            FontRenderContext fontRenderContext = new FontRenderContext(new AffineTransform(), false, false);
            GlyphVector glyphVector = dynamicFont.createGlyphVector(fontRenderContext, "");

            //获取font中字形的映射关系，字段为private，使用反射
            Class<?> clazz = Font.class;
            Field[] fs = clazz.getDeclaredFields();
            Font2DHandle font2DHandle = null;
            for (int i = 0; i < fs.length; i++) {
                fs[i].setAccessible(true);// 将目标属性设置为可以访问
                if (fs[i].getName().equals("font2DHandle")) {
                    font2DHandle = (Font2DHandle) fs[i].get(dynamicFont);
                }

            }

            //得到映射关系
            Font2D font2D = font2DHandle.font2D;
            TrueTypeFont trueTypeFont = (TrueTypeFont) font2D;
            TrueTypeGlyphMapper charToGlyphMapper = (TrueTypeGlyphMapper) trueTypeFont.getMapper();

            //开始解密，encodeString为加密后的字符串
            StringBuffer buffer = new StringBuffer();
            char[] chars = encodeString.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                buffer.append(charToGlyphMapper.charToGlyph(chars[i]) - 1);
            }
            return buffer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}
